%{
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "Common.h"


extern int yyleng;
extern char *yytext;
unsigned int row = 0, col = 0;
char identifierBuff[IDENTIFIER_NAME_MAX_LEN];
unsigned int identifierLen;
unsigned int instructionCount = 0;

union yystype_union
{
	struct Instruction* instruction;

	struct Identifier* identifier;

	enum REGISTER reg;

	enum JMP_CONDITION jmpCondition;

	Float f;
	Float32 f32;
	Float64 f64;

	Int s;
	Int16 s16;
	Int32 s32;

	UInt u;
	UInt16 u16;
	UInt32 u32;
};

#define YYSTYPE union yystype_union
void yyerror(const char* msg);

#define BISON_PRINT(STRING, ...) printf("BISON: " STRING "\n", __VA_ARGS__)
#define FLEX_PRINT(STRING, ...) printf("FLEX : " STRING "\n", __VA_ARGS__)

%}

%token INT_CONST UINT_CONST FLOAT_CONST
%token CONSTANT IDENTIFIER

// Operations
%token INC JMP MOV ADD SUB RET CMP

// Registers
%token EAX AX AH AL EBX BX BH BL ECX CX CH CL EDX DX DH DL ESI SI EDI DI EBP BP ESP SP
%token JA JAE JB JBE JE JG JGE JL JLE JNA JNAE JNB JNBE JNE JNG JNGE JNL JNLE JNZ

%start translation_unit
%%

translation_unit
	: /* empty */
	{
		BISON_PRINT("translation_unit -> /* empty */");
	}

	| '\n' translation_unit
	{
		BISON_PRINT("translation_unit -> '\\n'");
	}

	| instruction '\n' translation_unit
	{
		++instructionCount;

		BISON_PRINT("translation_unit -> instruction '\\n' translation_unit");
	}
	;

const_int_or_uint
	: INT_CONST  { $$.s = $1.s; }
	| UINT_CONST { $$.u = $1.u; }
	;

MOV [EBP - 2], FFFFh

register_constant_arg : IDENTIFIER | register | constant ;

MOV '[' register effective ']'

register_constant_arg_list
	: /* empty */ {}
	| register_constant_arg '+' register_constant_arg register_constant_arg_list {}
	| register_constant_arg '-' register_constant_arg register_constant_arg_list {}
	;

instruction
	: MOV register ',' constant
	{
		struct MovConstantToRegister* p = ADD_INSTRUCTION(struct MovConstantToRegister, OT_MOV_CONSTANT_TO_REGISTER);
		p->dest = $2.reg;
		p->src = $4.s;
		
		BISON_PRINT("MOV register ',' constant");
	}

	: MOV register ',' '[' register_constant_arg_list ']'
	{
		BISON_PRINT("MOV register ',' '[' register_constant_arg_list ']'");
	}

	| JMP const_int_or_uint
	{
		struct JmpConstant* p = ADD_INSTRUCTION(struct JmpConstant, OT_JMP_CONSTANT);
		p->constant = $2.s;

		BISON_PRINT("JMP INT_CONST");
	}

	| IDENTIFIER ':'
	{
		ADD_IDENTIFIER(identifierBuff, IDT_JMP_LABEL, struct JmpLabel);
		
		BISON_PRINT("IDENTIFIER ':'");
	}

	| JMP IDENTIFIER
	{
		struct Identifier* i = identifiers;
		while (i)
		{
			switch (i->type)
			{
			case IDT_JMP_LABEL:
				{
					if (strcmp(identifierBuff, i->ascii) == 0)
					{
						struct JmpLabel* p = ADD_INSTRUCTION(struct JmpLabel, OT_JMP_LABEL);
						p->label = i;
					}

					(UInt8*)i += (sizeof(struct Identifier) + sizeof(JmpLabel));
					break;
				}
			case IDT_CONSTANT:
				{
					(UInt8*)i += (sizeof(struct Identifier));
					break;
				}
			}
		}

		BISON_PRINT("JMP IDENTIFIER");
	}

	| jmp_condition const_int_or_uint
	{
		struct JmpConstantIfCondition* p = ADD_INSTRUCTION(struct JmpConstantIfCondition, OT_JMP_CONSTANT_IF_CONDITION);
		p->condition = $1.jmpCondition;
		p->constant = $2.s;

		BISON_PRINT("jmp_condition const_int_or_uint");
	}

	| CMP register ',' const_int_or_uint
	{
		struct CmpRegisterAndConstant* p = ADD_INSTRUCTION(struct CmpRegisterAndConstant, OT_CMP_REGISTER_AND_CONSTANT);
		p->reg = $2.reg;
		p->constant = $4.s;

		BISON_PRINT("CMP register ',' const_int_or_uint");
	}

	| INC register
	{
		struct IncRegister* p = ADD_INSTRUCTION(struct IncRegister, OT_INC_REGISTER);
		p->reg = $2.reg;

		BISON_PRINT("INC register");
	}

	| ADD register ',' constant
	{
		struct AddConstantToRegister* p = ADD_INSTRUCTION(struct AddConstantToRegister, OT_ADD_CONSTANT_TO_REGISTER);
		p->dest = $2.reg;
		p->src = $4.s;

		BISON_PRINT("ADD register ',' constant");
	}

	| SUB register ',' constant
	{
		struct SubConstantFromRegister* p = ADD_INSTRUCTION(struct SubConstantFromRegister, OT_SUB_CONSTANT_FROM_REGISTER);
		p->dest = $2.reg;
		p->src = $4.s;

		BISON_PRINT("SUB register ',' constant");
	}

	| RET
	{
		ADD_BASIC_INSTRUCTION(OT_RET);
		BISON_PRINT("RET");
	}
	;

jmp_condition
	: JA   { $$.jmpCondition = JA - JA; }
	| JAE  { $$.jmpCondition = JAE - JA; }
	| JB   { $$.jmpCondition = JB - JA; }
	| JBE  { $$.jmpCondition = JBE - JA; }
	| JE   { $$.jmpCondition = JE - JA; }
	| JG   { $$.jmpCondition = JG - JA; }
	| JGE  { $$.jmpCondition = JGE - JA; }
	| JL   { $$.jmpCondition = JL - JA; }
	| JLE  { $$.jmpCondition = JLE - JA; }
	| JNA  { $$.jmpCondition = JNA - JA; }
	| JNAE { $$.jmpCondition = JNAE - JA; }
	| JNB  { $$.jmpCondition = JNB - JA; }
	| JNBE { $$.jmpCondition = JNBE - JA; }
	| JNE  { $$.jmpCondition = JNE - JA; }
	| JNG  { $$.jmpCondition = JNG - JA; }
	| JNGE { $$.jmpCondition = JNGE - JA; }
	| JNL  { $$.jmpCondition = JNL - JA; }
	| JNLE { $$.jmpCondition = JNLE - JA; }
	| JNZ  { $$.jmpCondition = JNZ - JA; }
	;

constant
	: INT_CONST
	{
		$$.s = $1.s;
		BISON_PRINT("constant -> INT_CONST (%d)", $1.s);
	}

	| UINT_CONST
	{
		$$.u = $1.u;
		BISON_PRINT("constant -> UINT_CONST (%u)", $1.u);
	}

	| FLOAT_CONST
	{
		$$.f = $1.f;
		BISON_PRINT("constant -> FLOAT_CONST (%f)", $1.f);
	}
	;

register
	: EAX { $$.reg = R_EAX; }
	| AX  { $$.reg = R_AX; }
	| AH  { $$.reg = R_AH; }
	| AL  { $$.reg = R_AL; }
	| EBX { $$.reg = R_EBX; }
	| BX  { $$.reg = R_BX; }
	| BH  { $$.reg = R_BH; }
	| BL  { $$.reg = R_BL; }
	| ECX { $$.reg = R_ECX; }
	| CX  { $$.reg = R_CX; }
	| CH  { $$.reg = R_CH; }
	| CL  { $$.reg = R_CL; }
	| EDX { $$.reg = R_EDX; }
	| DX  { $$.reg = R_DX; }
	| DH  { $$.reg = R_DH; }
	| DL  { $$.reg = R_DL; }
	| ESI { $$.reg = R_ESI; }
	| SI  { $$.reg = R_SI; }
	| EDI { $$.reg = R_EDI; }
	| DI  { $$.reg = R_DI; }
	| EBP { $$.reg = R_EBP; }
	| BP  { $$.reg = R_BP; }
	| ESP { $$.reg = R_ESP; }
	| SP  { $$.reg = R_SP; }
	;

%%
#include "lex.yy.c"